// This is a small shell that loads a DLL calls its well known entry point.
// The intention is to only depend on OS mechanisms and avoid the LIBC
// dependency completely.
// This indirection is done primarily to:
// Play nicely with software firewalls. Most firewalls watch the executable
// module making network requests and we do not want them to notify the user
// after we've updated the program. The DLL can change independently of the
// shell and we expect the shell to remain unchanged most of the time.
//
// Changes to this executable will not appear in offical builds until they are
// included in an offical build and the resulting file is checked in to the
// saved shell location.

// Disable the RTC checks because this shell doesn't build with a CRT.
#pragma runtime_checks("", off)

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <atlstr.h>

#include "../pcagent/main.h"
#include "../base/basictypes.h"

namespace pcagent_helper
{
	const CString kPCAgentDllName = _T("pcagent.dll");

	// Disable the stack checks to keep the code size down.
	// The check_stack pragma did not really work. The stack checks had to be
	// disabled in the mk_file.
#pragma check_stack()

	HRESULT HRESULTFromLastError() {
		DWORD error_code = ::GetLastError();
		return (error_code != NO_ERROR) ? HRESULT_FROM_WIN32(error_code) : E_FAIL;
	}

	// Apapted from File::Exist in file.cpp
	bool FileExists(const TCHAR* file_name)
	{
		if (!file_name || !*file_name)
			return false;

		WIN32_FILE_ATTRIBUTE_DATA attrs = {0};
		return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs);
	}

	HRESULT GetDllPath(HINSTANCE instance, bool is_machine, CString* dll_path)
	{
		TCHAR base_path[MAX_PATH] = {0};
		TCHAR path[MAX_PATH] = {0};

		if (!::GetModuleFileName(instance, base_path, arraysize(base_path)))
			return HRESULTFromLastError();

		::PathRemoveFileSpec(base_path);

		// Try the side-by-side DLL first.
		_tcscpy_s(path, arraysize(path), base_path);
		if (!::PathAppend(path, kPCAgentDllName))
			return HRESULTFromLastError();
		
		if (FileExists(path))
		{
			*dll_path = path;
			return S_OK;
		}

		return E_FAIL;
	}
}


int WINAPI _tWinMain(HINSTANCE instance,
					 HINSTANCE,
					 LPTSTR,
					 int cmd_show)
{
	HRESULT hr;

	CString dll_path;
	hr = pcagent_helper::GetDllPath(instance, false, &dll_path);
	if (FAILED(hr))
		return hr;

	HMODULE module(::LoadLibraryEx(dll_path, NULL, 0));
	if (!module)
		return pcagent_helper::HRESULTFromLastError();

	DllEntry dll_entry = reinterpret_cast<DllEntry>(::GetProcAddress(module, "DllEntry"));
	if (dll_entry)
		hr = dll_entry(::GetCommandLine(), cmd_show);
	else
		hr = E_FAIL;

	::FreeLibrary(module);

	return hr;
}